package pishen;

import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Set;

import edu.uci.ics.jung.algorithms.cluster.WeakComponentClusterer;
import edu.uci.ics.jung.algorithms.shortestpath.DistanceStatistics;
import edu.uci.ics.jung.algorithms.shortestpath.UnweightedShortestPath;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.UndirectedSparseGraph;

public class GraphHolder {
	private Graph<Node, Edge> graph;
	private GraphEvolver graphEvolver;
	private BufferedWriter gcValueWriter;
	private BufferedWriter densificationWriter;
	private BufferedWriter diameterWriter;
	private AverageClusteringCoefficientCalculator<Node, Edge> ccCalculator;
	private BufferedWriter ccWriter;
	private ShortestPathCalculator<Node, Edge> spCalculator;
	private BufferedWriter spWriter;
	private AssortativityCalculator<Node, Edge> assorCalculator;
	private BufferedWriter assorWriter;
	private AlphaCalculator<Node, Edge> alphaCalculator;
	private BufferedWriter alphaWriter;
	
	public void startSimulate(){
		graph = new UndirectedSparseGraph<Node, Edge>();
		graphEvolver = new GraphEvolver(graph);
		openOutputFiles();
		ccCalculator = new AverageClusteringCoefficientCalculator<Node, Edge>(graph);
		spCalculator = new ShortestPathCalculator<Node, Edge>(graph);
		assorCalculator = new AssortativityCalculator<Node, Edge>(graph);
		alphaCalculator = new AlphaCalculator<Node, Edge>(graph, 10000, 3);
		graphEvolver.initialize(3);
		checkCC();
		checkGC();
		checkDensification();
		checkSPAndDiameter();
		checkAssortativity();
		int numOfHundred = 0;
		int fake = 330;
		while(graph.getVertexCount() < 10000){
			graphEvolver.step();
			
			checkCC();
			checkGC();
			checkDensification();
			/*
			if(graph.getVertexCount() / 500 > numOfHundred){
				checkSPAndDiameter();
				numOfHundred++;
			}
			*/
			
			int currentTime = graphEvolver.getTime();
			if(currentTime == fake / 2 || currentTime == fake * 3 / 4 || currentTime == fake * 7 / 8){
				checkPowerLaw();
			}
			checkAssortativity();
			System.out.println("time=" + graphEvolver.getTime() + ", node=" + graph.getVertexCount());
		}
		//saveGraph("final_graph");
		checkPowerLaw();
		checkLifeTime();
		checkAgeDis();
		closeOutputFiles();
	}
	
	private void openOutputFiles(){
		try {
			gcValueWriter = new BufferedWriter(new FileWriter("gc_value"));
			densificationWriter = new BufferedWriter(new FileWriter("densification"));
			densificationWriter.write("time/node/edge\n");
			diameterWriter = new BufferedWriter(new FileWriter("diameter"));
			ccWriter = new BufferedWriter(new FileWriter("cc_value"));
			spWriter = new BufferedWriter(new FileWriter("shortest_path"));
			assorWriter = new BufferedWriter(new FileWriter("assortativity"));
			alphaWriter = new BufferedWriter(new FileWriter("power_law"));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void closeOutputFiles(){
		try {
			gcValueWriter.close();
			densificationWriter.close();
			diameterWriter.close();
			ccWriter.close();
			spWriter.close();
			assorWriter.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void checkLifeTime(){
		System.out.println("checking life time");
		try {
			BufferedWriter out = new BufferedWriter(new FileWriter("life_time"));
			int[] lifeTime = graphEvolver.getLifeTime();
			for(int i = 0; i < lifeTime.length; i++){
				out.write(i + " " + lifeTime[i]);
				out.newLine();
			}
			out.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void checkAgeDis(){
		System.out.println("checking age dis");
		int[] ageDis = new int[100];
		for(Node node: graph.getVertices()){
			ageDis[node.getGivenLife() - node.getRemainLife()]++;
		}
		try {
			BufferedWriter out = new BufferedWriter(new FileWriter("age_dis"));
			for(int i = 0; i < ageDis.length; i++){
				out.write(i + " " + ageDis[i]);
				out.newLine();
			}
			out.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void checkPowerLaw(){
		try {
			double alpha = alphaCalculator.run();
			alphaWriter.write("time=" + graphEvolver.getTime() + " alpha=" + alpha + " KL=" + alphaCalculator.getKL_Distance());
			alphaWriter.newLine();
			alphaWriter.write("degree/number of nodes");
			alphaWriter.newLine();
			//sum the degree
			int[] degreeCount = new int[graph.getVertexCount()];
			for(Node node: graph.getVertices()){
				degreeCount[graph.degree(node)]++;
			}
			//write the result
			for(int i = 0; i < degreeCount.length; i++){
				alphaWriter.write(i + " " + degreeCount[i]);
				alphaWriter.newLine();
			}
			alphaWriter.newLine();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void checkCC(){
		try {
			ccWriter.write(graphEvolver.getTime() + " " + ccCalculator.getCC());
			ccWriter.newLine();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void checkSPAndDiameter(){
		try {
			spWriter.write(graphEvolver.getTime() + " " + spCalculator.getAverageDistance());
			spWriter.newLine();
			spWriter.flush();
			diameterWriter.write(graphEvolver.getTime() + " " + spCalculator.getDiameter());
			diameterWriter.newLine();
			diameterWriter.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void checkAssortativity(){
		try {
			assorWriter.write(graphEvolver.getTime() + " " + assorCalculator.run());
			assorWriter.newLine();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void checkGC(){
		WeakComponentClusterer<Node, Edge> wcc = new WeakComponentClusterer<Node, Edge>();
		Set<Set<Node>> componentSet = wcc.transform(graph);
		int max = 0;
		for(Set<Node> nodeSet: componentSet){
			if(nodeSet.size() > max){
				max = nodeSet.size();
			}
		}
		try {
			gcValueWriter.write(graphEvolver.getTime() + " " + (max / (double)graph.getVertexCount()));
			gcValueWriter.newLine();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void checkDensification(){
		try {
			densificationWriter.write(graphEvolver.getTime() + " " + graph.getVertexCount() + " " + graph.getEdgeCount());
			densificationWriter.newLine();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void saveGraph(String fileName){
		System.out.println("start storing graph");
		ObjectOutputStream objectOut;
		try {
			objectOut = new ObjectOutputStream(new FileOutputStream(fileName));
			objectOut.writeObject(graph);
			objectOut.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("finish storing graph");
	}
	
	private void readGraph(String fileName){
		ObjectInputStream objectIn;
		try {
			objectIn = new ObjectInputStream(new FileInputStream(fileName));
			graph = (Graph<Node, Edge>)objectIn.readObject();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}
